#include "ExtractionManager.h"

ExtractionManager::ExtractionManager(QObject *parent) : QThread(parent){
    this->extractions = NULL;
    this->waitingExtractions = NULL;
    this->getAudioSignalInThread = NULL;
    this->connect(this, SIGNAL(newExtractionAdded(int,int)), this, SLOT(waitAndEventuallyAddWaitingExtraction()));
}
void ExtractionManager::stopAndReinitialise(){
    this->dontStop = false;
    //this->extractions->clear();
    //this->waitingExtractions->clear();
    if(this->getAudioSignalInThread != NULL){
        this->getAudioSignalInThread->stopAndFreeMemory();
        delete this->getAudioSignalInThread;
    }
    delete this->waitingExtractions;
    delete this->extractions;
    this->extractions = NULL;
    this->waitingExtractions = NULL;
    this->getAudioSignalInThread = NULL;
}

void ExtractionManager::startInformationExtraction(QString videoFileName){
    //if(this->getAudioSignalInThread->isRunning()){
        //this->getAudioSignalInThread->stopAndFreeMemory();
    //}

    this->stopAndReinitialise();
    this->getAudioSignalInThread = new GetAudioSignalInThread();
    this->connect(this->getAudioSignalInThread, SIGNAL(dataExtracted()), this, SLOT(getInformationsAndEventuallyAddWaitingExtraction()));
    this->getAudioSignalInThread->extractInformationsIntoTab(videoFileName, &waveForm);
    delete this->waitingExtractions;
    delete this->extractions;
    this->waitingExtractions = new QQueue<WaitingExtractionInformation>();
    this->extractions = new QList<Extraction>();
    //emit waveFormGot();
}

void ExtractionManager::getInformationsAndEventuallyAddWaitingExtraction(){
    this->getInformations();
    this->eventuallyAddWaitingExtraction();
}


void ExtractionManager::getInformations(){
    //this->waveForm = this->getAudioSignalInThread->getWaveForm();
    this->fps = this->getAudioSignalInThread->getFps();
    this->nFrames = this->getAudioSignalInThread->getNFrames();
}

//TODO connect the following method
void ExtractionManager::eventuallyAddWaitingExtraction(){
    if(this->getAudioSignalInThread->isRunning()){
        this->getAudioSignalInThread->wait();
    }
    this->dontStop = true;
    if(!this->waitingExtractions->isEmpty() && dontStop){
        WaitingExtractionInformation extractionInformation = this->waitingExtractions->dequeue();
        switch(extractionInformation.idCutterMethod){
            case 0:
                this->addNewExtractionFromAutomaticMethod(extractionInformation.position, extractionInformation.nSecsMaxBefore, extractionInformation.nSecsMaxAfter, extractionInformation.sensibility, extractionInformation.medianDataSize);
            break;
            case 1:
                this->addNewExtractionFromSimpleMethod(extractionInformation.position, extractionInformation.nSecsMaxBefore, extractionInformation.nSecsMaxAfter);
            break;
        }
    }
}

void ExtractionManager::waitAndEventuallyAddWaitingExtraction(){
    this->wait();
    this->eventuallyAddWaitingExtraction();
}

void ExtractionManager::addNewExtractionFromAutomaticMethod(int position, int nSecsMaxBefore, int nSecsMaxAfter, double sensibility, int medianDataSize){
    this->_setIdCutterMethod(0); //TODO create enumeration
    this->_setAutomaticCuttingMethodParams(position, nSecsMaxBefore, nSecsMaxAfter, sensibility, medianDataSize);
    if(this->isRunning() || this->getAudioSignalInThread->isRunning()){
        this->_addExtractionInWaitingList(0, position, nSecsMaxBefore, nSecsMaxAfter, sensibility, medianDataSize);
    }else{
        this->dontStop = true;
        this->start();
        this->setPriority(QThread::HighPriority);
    }
}

void ExtractionManager::addNewExtractionFromSimpleMethod(int position, int nSecsMaxBefore, int nSecsMaxAfter){
    this->_setIdCutterMethod(1); //TODO create enumeration
    this->_setSimpleCuttingMethodParams(position, nSecsMaxBefore, nSecsMaxAfter);
    if(this->isRunning() || this->getAudioSignalInThread->isRunning()){
        this->_addExtractionInWaitingList(1, position, nSecsMaxBefore, nSecsMaxAfter);
    }else{
        this->dontStop = true;
        this->start();
        this->setPriority(QThread::HighPriority);
    }
}

void ExtractionManager::_addExtractionInWaitingList(int idCutterMethod, int position, int nSecsMaxBefore, int nSecsMaxAfter, double sensibility, int medianDataSize){
    WaitingExtractionInformation extractionInformation;
    extractionInformation.idCutterMethod = idCutterMethod;
    extractionInformation.position = position;
    extractionInformation.nSecsMaxBefore = nSecsMaxBefore;
    extractionInformation.nSecsMaxAfter = nSecsMaxAfter;
    extractionInformation.sensibility = sensibility;
    extractionInformation.medianDataSize = medianDataSize;
    this->waitingExtractions->enqueue(extractionInformation);
}

void ExtractionManager::run(){
    emit calculingExtraction();
    Extraction extraction;
    switch(this->idCutterMethod){
        case 0:{ //TODO enum + set a good algorithm + create new method
            //this->medianDataSize = 6;
            //float this->sensibility = 0.60;

            float medianAtPosition = this->getMedianAudioData(this->position, this->medianDataSize);
            //Looking for start
            int maxStart = this->position - this->fps*this->nSecsBefore;
            if(maxStart < 0){
                maxStart = 0;
            }
            int start = this->position - 1;
            float startMedian = this->getMedianAudioData(start, this->medianDataSize);
            while(start>maxStart && startMedian > this->sensibility*medianAtPosition){
                //std::cout << "================" << std::endl;
                //std::cout << "medianAtPosition :" << medianAtPosition << std::endl;
                //std::cout << "startMedian :" << startMedian << std::endl;
                start--;
                startMedian = this->getMedianAudioData(start, this->medianDataSize);
            }
            //std::cout << "start :" << start << ", min :" << maxStart << std::endl;

            int maxEnd = this->position + this->fps*this->nSecsBefore;
            if(maxStart >= this->nFrames){
                maxStart = this->nFrames;
            }
            int end = this->position + 1;
            float endMedian = this->getMedianAudioData(end, this->medianDataSize);
            while((end < maxEnd) && (endMedian > this->sensibility*medianAtPosition)){
                end++;
                endMedian = this->getMedianAudioData(start, this->medianDataSize);
            }
            extraction.start = start;
            extraction.end = end;
            break;
        }
        case 1:{
            int beforeInFrame = this->fps*this->nSecsBefore;
            int afterInFrame = this->fps*this->nSecsAfter;
            extraction.start = this->position - beforeInFrame;
            if(extraction.start < 0){
                    extraction.start = 0;
            }
            extraction.end = this->position + afterInFrame;
            if(extraction.end > this->nFrames){
                    extraction.end = this->nFrames;
            }
            break;
        }
    }
    this->appendExtraction(extraction);
    emit newExtractionAdded(extraction.start, extraction.end);
}

Extraction ExtractionManager::getExtraction(int id){
    return this->extractions->at(id);
}

Extraction ExtractionManager::getLastExtraction(){
    return this->extractions->last();
}

int ExtractionManager::getNExtractions(){
    if(this->extractions == NULL){
        return 0;
    }
    return this->extractions->length();
}

void ExtractionManager::removeExtraction(int id){
    this->extractions->removeAt(id);
}

void ExtractionManager::replaceExtraction(int indice, Extraction newExtraction){
    this->extractions->replace(indice, newExtraction);
}

void ExtractionManager::appendExtraction(Extraction extraction)
{
    this->extractions->append(extraction);
}

void ExtractionManager::_setIdCutterMethod(int idCutterMethod)
{
    this->idCutterMethod = idCutterMethod;
}

void ExtractionManager::_setSimpleCuttingMethodParams(int position, int nSecsBefore, int nSecsAfter){
    this->position = position;
    this->nSecsBefore = nSecsBefore;
    this->nSecsAfter = nSecsAfter;
}

void ExtractionManager::_setAutomaticCuttingMethodParams(int position, int nMaxSecsBefore, int nMaxSecsAfter, double sensibility, int medianDataSize){
    this->position = position;
    this->nSecsBefore = nMaxSecsAfter;
    this->nSecsAfter = nMaxSecsBefore;
    this->sensibility = sensibility;
    this->medianDataSize = medianDataSize;
}

float ExtractionManager::getMedianAudioData(int idFrame, int sizeData){
    int halfSize = sizeData/2;
    int first = idFrame - halfSize;
    int last = idFrame + halfSize;
    if(first < 0){
        first = 0;
    }
    if(last >= this->nFrames){
        last = this->nFrames-1;
    }
    QVector<float> sortedData;
    for(int i=0; i<=sizeData; i++){
        sortedData << this->waveForm[first+i];
    }
    qSort(sortedData);
    return sortedData.at((last-first)/2);
}

bool ExtractionManager::isCalculatingExtraction(){
    return this->isRunning() || this->waitingExtractions->count() > 0 ;
}

bool ExtractionManager::isCalculatingWaveform(){
    return this->getAudioSignalInThread != NULL && this->getAudioSignalInThread->isRunning();
}



